#include "MCEllipseVisual.h"
#include <MCGripPoint.h>
#include <MCSettings.h>
#include <MCLineSegment2D.h>

MCEllipseVisual::MCEllipseVisual(ICanvas *presenter, const MCEllipse2D &ellipse) :
    MCGraphicsItem(presenter),
    _ellipseModel(ellipse)
{
    initGripPoints();
}

void MCEllipseVisual::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    setPainter(painter);

    drawEllipse(transformed().center(), transformed().rx(), transformed().ry());
}

bool MCEllipseVisual::contains(const QPointF &point) const
{
    MCEllipse2D model = transformed();
    MCPoint2D center = model.center();
    double d = std::sqrt(std::pow(point.x() - center.x, 2) + std::pow(point.y() - center.y, 2));

    return d < model.rx() && d < model.ry();
}

QPainterPath MCEllipseVisual::shape() const
{
    QPainterPath path;

    MCPoint2D center = transformed().center();
    double rx = transformed().rx();
    double ry = transformed().ry();

    auto centerDragZone = MCSettings::CircleClickableZone;

    path.addEllipse(QPointF(center.x, center.y), centerDragZone, centerDragZone);
    path.addEllipse(QPointF(center.x, center.y), rx + centerDragZone, ry + centerDragZone);
    path.addEllipse(QPointF(center.x, center.y), rx - centerDragZone, ry - centerDragZone);

    return path;
}

void MCEllipseVisual::setGripAt(IGripPoint *grip, const MCPoint2D &pos)
{
    if (grip->index() == 0)
    {
        _ellipseModel.move(pos - _ellipseModel.center());
    }
    else if (grip->index() > 0 && grip->index() <= 2)
    {
        _ellipseModel.update(_ellipseModel.center(), MCLineSegment2D(pos, _ellipseModel.center()).length(), _ellipseModel.ry());
    }
    else if (grip->index() > 2 && grip->index() <= 4)
    {
        _ellipseModel.update(_ellipseModel.center(), _ellipseModel.rx(), MCLineSegment2D(pos, _ellipseModel.center()).length());
    }

    resetGrips();
}

void MCEllipseVisual::resetGrips()
{
    gripPoints[0]->update(_ellipseModel.center());
    gripPoints[1]->update(_ellipseModel.center().x + _ellipseModel.rx(), _ellipseModel.center().y);
    gripPoints[2]->update(_ellipseModel.center().x - _ellipseModel.rx(), _ellipseModel.center().y);
    gripPoints[3]->update(_ellipseModel.center().x, _ellipseModel.center().y + _ellipseModel.ry());
    gripPoints[4]->update(_ellipseModel.center().x, _ellipseModel.center().y - _ellipseModel.ry());
}

MCEllipse2D MCEllipseVisual::transformed() const
{
    MCPoint2D center = presenter->screenToWorld(_ellipseModel.center());
    double rx = _ellipseModel.rx() / presenter->scale();
    double ry = _ellipseModel.ry() / presenter->scale();

    return MCEllipse2D(center, rx, ry);
}

void MCEllipseVisual::initGripPoints()
{
    gripPoints.push_back(new MCGripPoint(this, CenterPoint, 0, _ellipseModel.center()));
    gripPoints.push_back(new MCGripPoint(this, RadiusPoint, 1, MCPoint2D(_ellipseModel.center().x + _ellipseModel.rx(), _ellipseModel.center().y)));
    gripPoints.push_back(new MCGripPoint(this, RadiusPoint, 2, MCPoint2D(_ellipseModel.center().x - _ellipseModel.rx(), _ellipseModel.center().y)));
    gripPoints.push_back(new MCGripPoint(this, RadiusPoint, 3, MCPoint2D(_ellipseModel.center().x, _ellipseModel.center().y + _ellipseModel.ry())));
    gripPoints.push_back(new MCGripPoint(this, RadiusPoint, 4, MCPoint2D(_ellipseModel.center().x, _ellipseModel.center().y - _ellipseModel.ry())));
}

MCEllipse2D MCEllipseVisual::ellipse() const
{
    return _ellipseModel;
}

void MCEllipseVisual::update(const MCPoint2D &center, double rx, double ry)
{
    _ellipseModel = MCEllipse2D(center, rx, ry);
}

void MCEllipseVisual::update(const MCEllipse2D &ellipse)
{
    _ellipseModel = ellipse;
}
